/**
 * 
 */
package ru.olympus.antonyhands.ao.server.db;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;

import ru.olympus.antonyhands.ao.server.item.CraftInfo;
import ru.olympus.antonyhands.ao.server.item.CraftSkill;
import ru.olympus.antonyhands.ao.server.item.Item;
import ru.olympus.antonyhands.ao.server.item.ItemCategory;
import ru.olympus.antonyhands.ao.server.item.ItemExchange;
import ru.olympus.antonyhands.ao.server.item.ItemExchangeBase;
import ru.olympus.antonyhands.ao.server.item.ItemQuantityPair;
import ru.olympus.antonyhands.ao.server.item.ItemQuantityPairCollection;
import ru.olympus.antonyhands.ao.server.item.util.ByTitleComparator;
import ru.olympus.antonyhands.ao.server.item.util.ItemByCategoryFilter;
import ru.olympus.antonyhands.ao.server.util.GoogleIterable;
import ru.olympus.antonyhands.ao.shared.util.StringPair;
import ru.olympus.antonyhands.ao.shared.util.StringUtilities;

/**
 * @author AntonyHands
 *
 */
public class AOData
{
    private static final String FILE_NAME = "webapps/AOHelperGWT/WEB-INF/res/data.xml";
    private static final String BACKUP_FILE_NAME = "webapps/AOHelperGWT/WEB-INF/res/data.bak";

    private static final AOData _instance = new AOData();

    private static boolean dataLoaded = false;
    
    private AOData()
    {
        persistent.put(ItemCategory.TAG_NAME, ItemCategory.class);
        persistent.put(CraftSkill.TAG_NAME, CraftSkill.class);
        persistent.put(Item.TAG_NAME, Item.class);
        persistent.put(ItemQuantityPairCollection.TAG_NAME, ItemQuantityPairCollection.class);
        persistent.put(ItemQuantityPair.TAG_NAME, ItemQuantityPair.class);
        persistent.put(CraftInfo.TAG_NAME, CraftInfo.class);
        persistent.put(ItemExchange.TAG_NAME, ItemExchange.class);
    }

    private void loadDataFromXML()
    {
        File file = new File(FILE_NAME);
        if (file.exists())
            try
            {
                SAXBuilder sb = new SAXBuilder();
                Document doc = sb.build(file);
                Element root = doc.getRootElement();
                for (Element child : (List<Element>)root.getChildren())
                {

                    IPersistentClass element = readXMLContent(child);
                    // положим в список
                    if (element instanceof ItemCategory)
                        itemCategories.add((ItemCategory)element);
                    else if (element instanceof CraftSkill)
                        craftSkills.add((CraftSkill)element);
                    else if (element instanceof Item)
                        items.add((Item)element);
                    else if (element instanceof ItemExchangeBase)
                    {
                        exchanges.add((ItemExchangeBase)element);
                        if (element instanceof CraftInfo)
                        {
                            CraftInfo info = (CraftInfo)element;
                            info.getResultItem().setCraftInfo(info);
                            for (Item item : info.listSourceItems())
                                item.addCraftableItem(info);
                        }
                    }
                }
            }
            catch (JDOMException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            catch (IOException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            catch (InstantiationException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            catch (IllegalAccessException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        else
        {
            System.out.println("no file");
        }
    }

    public static AOData getInstance()
    {
        if (!dataLoaded)
        {
            dataLoaded = true;
            _instance.loadDataFromXML();
        }
        return _instance;
    }

    // ==========================================================

    private Map<String, Class<? extends IPersistentClass>> persistent = new HashMap<String, Class<? extends IPersistentClass>>();

    private List<ItemCategory> itemCategories = new ArrayList<ItemCategory>();
    private List<CraftSkill> craftSkills = new ArrayList<CraftSkill>();
    private List<Item> items = new ArrayList<Item>();
    private List<ItemExchangeBase> exchanges = new ArrayList<ItemExchangeBase>();

    // ==========================================================

    public void doSave()
    {
        File backupFile = new File(BACKUP_FILE_NAME);
        File file = new File(FILE_NAME);
        if (file.exists())
        {
            if (backupFile.exists())
                backupFile.delete();
            file.renameTo(new File(BACKUP_FILE_NAME));
        }

        try
        {
            file = new File(FILE_NAME);
            file.createNewFile();
            FileOutputStream fs = new FileOutputStream(file);
            fs.write("<data></data>".getBytes("UTF-8"));
            fs.flush();
            fs.close();

            SAXBuilder sb = new SAXBuilder();
            Document doc = sb.build(file);
            Element root = doc.getRootElement();
            addXMLContent(root, itemCategories);
            addXMLContent(root, craftSkills);
            addXMLContent(root, items);
            addXMLContent(root, exchanges);

            fs = new FileOutputStream(file);
            new XMLOutputter().output(doc, fs);
            fs.flush();
            fs.close();
        }
        catch (JDOMException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
            file.delete();
        }
        catch (IOException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
            file.delete();
        }

    }

    private void addXMLContent(Element rootElement, Collection<? extends IPersistentClass> data)
    {
        for (IPersistentClass pc : data)
        {
            Element child = new Element(pc.getTagName());
            for (StringPair sp : pc.getAttributes())
            {
                child.setAttribute(sp.getKey(), sp.getValue());
            }
            if (pc.getPersistentChildren() != null)
                addXMLContent(child, pc.getPersistentChildren());
            rootElement.addContent(child);
        }
    }

    private IPersistentClass readXMLContent(Element element) throws InstantiationException, IllegalAccessException
    {
        IPersistentClass loadedObject = persistent.get(element.getName()).newInstance();
        List<StringPair> attrs = new ArrayList<StringPair>();
        for (Attribute attr : (List<Attribute>)element.getAttributes())
            attrs.add(new StringPair(attr.getName(), attr.getValue()));
        loadedObject.setAttributes(attrs);
        if (element.getChildren().size() > 0)
        {
            List<IPersistentClass> persistentChildren = new ArrayList<IPersistentClass>();
            for (Element childElement : (List<Element>)element.getChildren())
                persistentChildren.add(readXMLContent(childElement));
            loadedObject.setPersistentChildren(persistentChildren);
        }
        return loadedObject;
    }

    public List<ItemCategory> listItemCategories()
    {
        return itemCategories;
    }

    // public ItemCategory[] listItemCategories() {
    // return itemCategories.toArray(new ItemCategory[itemCategories.size()]);
    // }

    public boolean addItemCategory(ItemCategory newCategory)
    {
        for (ItemCategory category : itemCategories)
            if (category.getTitle().equals(newCategory.getTitle()) || category.getCode().equals(newCategory.getCode()))
                return false;
        itemCategories.add(newCategory);
        Collections.sort(itemCategories, new ByTitleComparator());
        return true;
    }

    public boolean addCraftSkill(CraftSkill newCraftSkill)
    {
        for (CraftSkill skill : craftSkills)
            if (skill.getTitle().equals(newCraftSkill.getTitle()) || skill.getCode().equals(newCraftSkill.getCode()))
                return false;
        craftSkills.add(newCraftSkill);
        Collections.sort(craftSkills, new ByTitleComparator());
        return true;
    }

    public boolean addItem(Item newItem)
    {
        for (Item item : items)
            if (item.getTitle().equals(newItem.getTitle()) || item.getCode().equals(newItem.getCode()))
                return false;
        items.add(newItem);
        return true;
    }

    public boolean addCraftInfo(CraftInfo craftInfo)
    {
        for (ItemExchangeBase ieb : exchanges)
            if ((ieb instanceof CraftInfo) && ieb.getResultItem().equals(craftInfo.getResultItem()))
                return false;
        exchanges.add(craftInfo);
        craftInfo.getResultItem().setCraftInfo(craftInfo);
        for (Item item : craftInfo.listSourceItems())
            item.addCraftableItem(craftInfo);
        return true;
    }

    // ==========================================================================================

    public Item getItemByCode(String code)
    {
        for (Item item : items)
            if (item.getCode().equals(code))
                return item;
        return null;
    }

    public CraftSkill getCraftSkillByCode(String code)
    {
        for (CraftSkill skill : craftSkills)
            if (skill.getCode().equals(code))
                return skill;
        return null;
    }

    public ItemCategory getItemCategoryByCode(String code)
    {
        for (ItemCategory category : itemCategories)
            if (category.getCode().equals(code))
                return category;
        return null;
    }

    public List<CraftSkill> listCraftSkills()
    {
        List<CraftSkill> list = new ArrayList<CraftSkill>();
        list.addAll(craftSkills);
        return list;
    }

    @Deprecated
    public List<Item> listItemsByCategoryCode(String categoryCode)
    {
        if (StringUtilities.isEmpty(categoryCode))
            return new ArrayList<Item>();
        ItemCategory cat = getItemCategoryByCode(categoryCode);
        if (cat == null)
            return new ArrayList<Item>();
        return listItemsByCategory(cat);
    }

    @Deprecated
    public List<Item> listItemsByCategory(ItemCategory category)
    {
        return GoogleIterable.with(items).filter(new ItemByCategoryFilter(category)).toArrayList();
    }

    public List<Item> listItems()
    {
    	return new ArrayList<Item>(items);
    }
}
